# coding=utf-8
from string import Template

import os
from typing import List
from utils.templete_file import TempleteFile
from parse.parse_types import Module
from utils.util import  writeFile



templete_h = Template(
"""#pragma once

#include <vector>
#include <transfer/common.h>
#include <arpa/inet.h>

#include "common.h"

namespace ecarx::transfer::utils{

    ipcp::IpcpValue CBToIpcpValue(const Signal &);

}
"""
)
templete_cpp = Template(
"""#include "convertCBToIPCPValue.h"
#include "vp/SignalServiceId.h"
#include "ap/signal_id.h"
#include <ipcp/ipcp.h>
#include <string.h>

${ecu_h}

namespace ecarx::transfer::utils{

    ipcp::IpcpValue CBToIpcpValue(const Signal & signal){
        ipcp::IpcpValue ipcp;
        switch(signal.signalId){
${case_blocks}
        }
        return ipcp;
    }
}
"""
)
case_tempelet = Template(
"""
            case static_cast<int32_t>(${id}):{
${codes}
            }
            break;
""")

class Case(TempleteFile):

    def __init__(self, id) -> None:
        self.id = id
        self.code = []

    def codeBlock(self, code: str):
        self.code = code
        return self

    def toStr(self) -> str:
        return case_tempelet.substitute(id=self.id, codes=self.code)

base_type_templete = Template(
"""
                ipcp.service_id=${service_id};
                ipcp.oper_id = ${op_id};
                ipcp.oper_type = IPCP_OPERATION_SETREQUEST_NORETURN;
                ipcp.payload_type = IPCP_DATA_NORMAL;
                memset(ipcp.peer_ip, 0, sizeof(ipcp.peer_ip));
                strcpy(ipcp.peer_ip, SERVICE_IP);
                ipcp.peer_port = SERVICE_PORT;
                ipcp.payload_len = sizeof(${send_struct_name});
                ${send_struct_name}* buffer = (${send_struct_name}*)new uint8_t[ipcp.payload_len];
                (*buffer).value=${value};
                ipcp.payload = (void*)buffer;
""")

struct_templete = Template(
"""
                ipcp.service_id=${service_id};
                ipcp.oper_id = ${op_id};
                ipcp.oper_type = IPCP_OPERATION_SETREQUEST_NORETURN;
                ipcp.payload_type = IPCP_DATA_NORMAL;
                memset(ipcp.peer_ip, 0, sizeof(ipcp.peer_ip));
                strcpy(ipcp.peer_ip, SERVICE_IP);
                ipcp.peer_port = SERVICE_PORT;
                ipcp.payload_len = sizeof(${send_struct_name});
                ${send_struct_name}* buffer = (${send_struct_name}*)new uint8_t[ipcp.payload_len];
                memcpy(buffer, signal.value.bytes.data(), ipcp.payload_len);
                ${members}
                ipcp.payload = (void*)buffer;
""")

array_templete = Template(
"""
                ipcp.service_id=${service_id};
                ipcp.oper_id = ${op_id};
                ipcp.oper_type = IPCP_OPERATION_SETREQUEST_NORETURN;
                ipcp.payload_type = IPCP_DATA_NORMAL;
                memset(ipcp.peer_ip, 0, sizeof(ipcp.peer_ip));
                strcpy(ipcp.peer_ip, SERVICE_IP);
                ipcp.peer_port = SERVICE_PORT;
                ipcp.payload_len = sizeof(${send_struct_name});
                ${send_struct_name}* buffer = (${send_struct_name}*)new uint8_t[ipcp.payload_len];
                memcpy(buffer, signal.value.bytes.data(), ipcp.payload_len);
                ${members}
                ipcp.payload = (void*)buffer;
""")
            
# 逐个处理所有的设置信号
member_templete = Template("""
                (*buffer).${member}=${value};""")




def generateCBToIPCPValue(modules:List[Module],outDir:str):
    module_headers = []
    cases=[]
    for module in modules:
        if len(module.cb_apis) > 0:
            module_headers.append("#include \"vp/{0}.h\"".format(module.name))
    
            for cb in module.cb_apis:

                case = Case("CBSignal::{0}".format(cb.api))
                
                payload=cb.getPayload()
                if payload.isBaseTypePayload():
                    basePayload = payload.getBaseTypePayload()
                    case.codeBlock(base_type_templete.substitute(
                        service_id="EcarXSignalServiceID::serviceID_{0}".format(module.name.upper()),
                        op_id="{0}OpID".format(cb.api.replace("_","").lower()),
                        send_struct_name=cb.get_send_struct_name(),
                        value = basePayload.toNet("{}signal.value.{}[0]".format(
                                    "({})".format(basePayload.type.name) if basePayload.type.value < 5 else "",
                                    cb.toSignalObjMember()
                                    )
                                )
                    ))
                elif payload.isStructPayload():
                    structPayload = payload.getStructPayload()
                    buffer_props=[]
                    for member in structPayload.members:
                        if member.is_array:
                            for i in range(member.array_len):
                                buffer_props.append(member_templete.substitute(
                                    member="value.{}[{}]".format(member.member_name,i),
                                    value = member.toNet("(*buffer).value.{}[{}]".format(member.member_name,i))
                                ))
                        else:
                            buffer_props.append(member_templete.substitute(
                                member="value.{}".format(member.member_name),
                                value =member.toNet("(*buffer).value.{}".format(member.member_name))
                            ))
                    case.codeBlock(struct_templete.substitute(
                        service_id="EcarXSignalServiceID::serviceID_{0}".format(module.name.upper()),
                        op_id="{0}OpID".format(cb.api.replace("_","").lower()),
                        send_struct_name=cb.get_send_struct_name(),
                        members="".join(buffer_props)
                    ))
                elif payload.isArrayPayload():
                    arrayPayload = payload.getArrayPayload()
                    buffer_props=[]
                    for i in range(int(arrayPayload.array_len)):
                        buffer_props.append(member_templete.substitute(
                            member="{}[{}]".format(arrayPayload.array_name,i),
                            value = arrayPayload.toNet("(*buffer).value[{index}]".format(index=i))
                        ))
                    case.codeBlock(array_templete.substitute(
                        service_id="EcarXSignalServiceID::serviceID_{0}".format(module.name.upper()),
                        op_id="{0}OpID".format(cb.api.replace("_","").lower()),
                        send_struct_name=cb.get_send_struct_name(),
                        members="".join(buffer_props)
                    ))
                cases.append(case)

    writeFile(os.path.join(outDir,"src"), "convertCBToIPCPValue.h",lambda file:file.write(templete_h.substitute()))
    writeFile(os.path.join(outDir,"src"), "convertCBToIPCPValue.cpp",lambda file:
        file.write(templete_cpp.substitute(ecu_h="\n".join(module_headers),case_blocks="".join([case.toStr() for case in cases]))))        

